﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.Collections.ObjectModel;

namespace WcfContrib.Metadata
{
    static class PolicyTagger
    {
        public static void TagImporter(WsdlImporter importer)
        {
            importer.PolicyImportExtensions.Insert(0, new PolicyTarggerImportExtension());
        }

        public static void TagImporter(MetadataImporter importer)
        {
            importer.PolicyImportExtensions.Insert(0, new PolicyTarggerImportExtension());
        }

        public static EndpointPolicyDefinitionSet GetPolicies(ServiceEndpoint endpoint)
        {
            return endpoint.Binding.GetProperty<EndpointPolicyDefinitionSet>(new BindingParameterCollection());
        }

        class PolicyTarggerImportExtension : IPolicyImportExtension
        {
            public void ImportPolicy(MetadataImporter importer, PolicyConversionContext context)
            {
                EndpointPolicyDefinitionSet definitions = new EndpointPolicyDefinitionSet
                {
                    Binding = new PolicyAssertionCollection(context.GetBindingAssertions())
                };

                Collection<OperationPolicyDescription> operationPolicies = new Collection<OperationPolicyDescription>();
                foreach (OperationDescription operationDescription in context.Contract.Operations)
                {
                    OperationPolicyDescription operationPolicy = new OperationPolicyDescription()
                    {
                        Description = operationDescription,
                        
                        Policies = new PolicyAssertionCollection(context.GetOperationBindingAssertions(operationDescription))
                    };

                    foreach (var item in operationDescription.Messages)
                    {
                        PolicyDefinition<MessageDescription> messagePolicy = new PolicyDefinition<MessageDescription>()
                        {
                            Description = item,

                            Policies = new PolicyAssertionCollection(context.GetMessageBindingAssertions(item))
                        };

                        operationPolicy.Messages.Add(messagePolicy);
                    }

                    foreach (var item in operationDescription.Faults)
                    {
                        PolicyDefinition<FaultDescription> faultPolicy = new PolicyDefinition<FaultDescription>()
                        {
                            Description = item,

                            Policies = new PolicyAssertionCollection(context.GetFaultBindingAssertions(item))
                        };

                        operationPolicy.Faults.Add(faultPolicy);
                    }

                    operationPolicies.Add(operationPolicy);
                }

                if (operationPolicies.Count > 0)
                {
                    definitions.Operations = operationPolicies;
                }

                //context.GetMessageBindingAssertions(
                //context.GetFaultBindingAssertions()
                context.BindingElements.Add(new PolicyTaggerBindingElement(definitions));
            }
        }

        class PolicyTaggerBindingElement : BindingElement
        {
            readonly EndpointPolicyDefinitionSet policies;
            public PolicyTaggerBindingElement(EndpointPolicyDefinitionSet policies)
            {
                this.policies = policies;
            }

            public override BindingElement Clone()
            {
                return new PolicyTaggerBindingElement(this.policies);
            }

            public override T GetProperty<T>(BindingContext context)
            {
                if (typeof(T) == typeof(EndpointPolicyDefinitionSet))
                {
                    return (T)(object)this.policies;
                }
                else
                {
                    return context.GetInnerProperty<T>();
                }
            }
        }
    }
}
